/*******************************************************************************
 * Copyright (c) 2007 University of Oslo.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     University of Oslo - initial API and implementation
 *******************************************************************************/

package com.cea.papyrus.diagram.sequence.sedi.model.command;

import static com.cea.papyrus.diagram.sequence.sedi.model.util.ModelUtil.createUniqueName;
import static com.cea.papyrus.diagram.sequence.sedi.model.util.ModelUtil.isDefaultName;

import org.eclipse.gef.commands.Command;
import org.eclipse.gef.commands.CompoundCommand;
import org.eclipse.uml2.uml.Interaction;
import org.eclipse.uml2.uml.Lifeline;
import org.eclipse.uml2.uml.Namespace;
import org.eclipse.uml2.uml.PartDecomposition;
import org.eclipse.uml2.uml.Property;
import org.eclipse.uml2.uml.UMLFactory;

/**
 * Command to set or remove a PartDecomposition for a Lifeline.
 * 
 * <p>
 * The following attributes must be set for the command to complete
 * successfully:
 * </p>
 * <ul>
 * <li>Lifeline
 * <li>Name
 * </ul>
 * 
 * @author frankd@ifi.uio.no
 */
public class LifelineDecomposeCommand extends Command {
	private String newText;
	private Lifeline lifeline;
	private boolean create;
	private String oldText;
	private PartDecomposition decomposition;
	private DelegatingCommand helpCmd;

	private CreateInteractionCommand createReferToCmd;

	public LifelineDecomposeCommand(final Lifeline lifeline, final String name) {
		this.lifeline = lifeline;
		this.newText = name;

		this.create = lifeline.getDecomposedAs() == null;

		final String action;
		if (!create && newText != null)
			action = "Modify";
		else if (newText == null)
			action = "Remove";
		else
			action = "Set";
		setLabel(action + " lifeline decomposition");
	}

	@SuppressWarnings("unchecked")
	@Override
	public void execute() {
		if (create) {
			decomposition = UMLFactory.eINSTANCE.createPartDecomposition();
			decomposition.setName(createUniqueName(lifeline.getInteraction()
					.getFragments(), "PartDecomposition_"));

			lifeline.getInteraction().getFragments().add(decomposition);
			lifeline.setDecomposedAs(decomposition);
		} else {
			decomposition = lifeline.getDecomposedAs();
			if (decomposition.getRefersTo() != null)
				oldText = decomposition.getRefersTo().getName();
		}

		if (newText != null) {
			final FindInteractionCommand findInteractionCmd = new FindInteractionCommand(
					lifeline.getModel(), newText);
			findInteractionCmd.execute();
			if (findInteractionCmd.getInteraction() != null) {
				CompoundCommand updateCmd = new CompoundCommand();
				updateCmd.add(new Command() {
					private Interaction oldRefTo;

					public @Override
					void execute() {
						oldRefTo = decomposition.getRefersTo();
						decomposition.setRefersTo(findInteractionCmd
								.getInteraction());
					}

					public @Override
					void undo() {
						decomposition.setRefersTo(oldRefTo);
						oldRefTo = null;
					}

					public @Override
					void dispose() {
						oldRefTo = null;
					}
				});

				Interaction oldRefersTo = decomposition.getRefersTo();
				if (oldRefersTo != null
						&& isDefaultName(oldRefersTo.getName(), "Interaction")) {
//					DeleteInteractionCommand delCmd = new DeleteInteractionCommand();
//					delCmd.setInteraction(decomposition.getRefersTo());
//					updateCmd.add(delCmd);
				}

				helpCmd = new DelegatingCommand();
				helpCmd.setCommand(updateCmd);
				helpCmd.execute();
			} else {
				Interaction currentRefersTo = decomposition.getRefersTo();
				if (currentRefersTo != null
						&& isDefaultName(currentRefersTo.getName(),
								"Interaction"))
					decomposition.getRefersTo().setName(newText);
				else {
					createReferToCmd = new CreateInteractionCommand();
					createReferToCmd
							.setNamespace(getReferredToInteractionNamespace());
					createReferToCmd.execute();
					createReferToCmd.getInteraction().setName(newText);
					decomposition
							.setRefersTo(createReferToCmd.getInteraction());
				}
			}
		} else {
			lifeline.setDecomposedAs(null);
			lifeline.getInteraction().getFragments().remove(decomposition);
		}
	}

	private Namespace getReferredToInteractionNamespace() {
		if (lifeline.getRepresents() instanceof Property
				&& ((Property) lifeline.getRepresents()).getType() instanceof Namespace) {
			Property represented = (Property) lifeline.getRepresents();
			return (Namespace) represented.getType();
		} else
			return lifeline.getInteraction().getContext();
	}

	@SuppressWarnings("unchecked")
	@Override
	public void undo() {
		if (newText == null) {
			lifeline.getInteraction().getFragments().add(decomposition);
			lifeline.setDecomposedAs(decomposition);
		}
		if (create) {
			lifeline.setDecomposedAs(null);
			lifeline.getInteraction().getFragments().remove(decomposition);

			if (createReferToCmd != null) {
				createReferToCmd.undo();
				createReferToCmd.dispose();
				createReferToCmd = null;
			}
		} else {
			if (helpCmd != null) {
				helpCmd.undo();
				helpCmd = null;
			} else {
				if (decomposition.getRefersTo() != null)
					decomposition.getRefersTo().setName(oldText);
				oldText = null;
			}
		}
		decomposition = null;
	}

	@Override
	public void redo() {
		this.execute();
	}

	@Override
	public void dispose() {
		lifeline = null;
		decomposition = null;
		newText = null;
		oldText = null;
	}
}
